/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2008-2011, Salzburg Research. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * - Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * - Neither the name of the LMF library client Project nor the names of its contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Contributor(s):
 * Stephanie Stroka
 *
 * File Info:
 * User: Stephanie Stroka
 * Date: 2011/08/19
 * Time: 18:04
 *
 * Project: lmflib
 */

package kiwi.facading.services;

import kiwi.config.ConfigurationServiceImpl;
import kiwi.config.api.ConfigurationService;
import kiwi.facading.api.ResourceService;
import kiwi.facading.api.TripleStore;
import kiwi.facading.exceptions.NamespaceResolvingException;
import kiwi.facading.exceptions.ResourceResolvingException;
import kiwi.facading.model.KiWiTriple;
import kiwi.facading.model.resources.*;
import kiwi.facading.utils.KiWiNamespaceUtils;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.openrdf.model.Statement;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFParseException;
import org.openrdf.sail.memory.MemoryStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URI;
import kiwi.facading.utils.MD5;
import kiwi.facading.model.Constants;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;

/**
 * @author Stephanie Stroka
 * User: sstroka
 * Date: 31.05.2011
 * Time: 16:17:03
 */
public class ResourceServiceImpl implements ResourceService {

    private Logger log =  LoggerFactory.getLogger(this.getClass());

    private ConfigurationService configurationService;

    private TripleStore tripleStore;

    private LiteralServiceImpl literalService;


    public ResourceServiceImpl() {
        tripleStore = new TripleStoreImpl();
        configurationService = new ConfigurationServiceImpl();
        literalService = new LiteralServiceImpl();
    }

    public KiWiUriResource createUriResource() {
        return createUriResource(configurationService.getBaseUri()+"resource/"+ UUID.randomUUID());
    }

    public KiWiUriResource createUriResource(KiWiUriResource context) {
        return createUriResource(configurationService.getBaseUri()+"resource/"+ UUID.randomUUID(), context);
    }

    public KiWiUriResource createUriResource(String uri) {
        return createUriResource(uri, null);
    }

    public KiWiUriResource createUriResource(String uri_str, KiWiUriResource context) {
        KiWiUriResource resource;
        if((resource = getUriResource(uri_str)) == null) {

            resource = new KiWiUriResource(uri_str);
            URI uri = configurationService.getResourceConnection(resource);
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost post = new HttpPost(uri);

            /*
            curl -i -X POST http://localhost:8080/LMF/resource/1234
             */

            try {

                HttpResponse response = httpClient.execute(post);

                int status = response.getStatusLine().getStatusCode();

                InputStream is = response.getEntity().getContent();

                log.info("status: {}", status);
                if(status != 201) {
                    log.error("Could not create resource {}: {}", uri_str, response.getStatusLine().getReasonPhrase());
                    return null;
                }

                BufferedReader in = new BufferedReader(
                        new InputStreamReader(is));

                StringBuilder response_msg = new StringBuilder();
                String s;
                while((s = in.readLine()) != null) {
                    response_msg.append(s);
                }
                log.info("response: {}", response_msg.toString());

            } catch (IOException e) {
                e.printStackTrace();
            } catch (HttpException e) {
                e.printStackTrace();
            }
        }
        resource.setContext(context);
        return resource;
    }

    public KiWiAnonResource createAnonResource() {
        return createAnonResource((KiWiUriResource) null);
    }

    public KiWiAnonResource createAnonResource(KiWiUriResource context) {
        return createAnonResource(Long.toHexString(
                System.currentTimeMillis())+Integer.toHexString((int) (Math.random()*1000)), context );
    }

    public KiWiAnonResource createAnonResource(String id) {
        return createAnonResource(id, null);
    }

    @Override
    public synchronized KiWiAnonResource createAnonResource(String id, KiWiUriResource context) {
        KiWiAnonResource result;
        if((result = getAnonResource(id)) == null) {

            URI uri = configurationService.getResourceConnection(result);
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost post = new HttpPost(uri);

            /*
            curl -i -X POST http://localhost:8080/LMF/resource/1234
             */


            try {
                HttpResponse response = httpClient.execute(post);
                
                int status = response.getStatusLine().getStatusCode();

                InputStream is = response.getEntity().getContent();

                log.info("status: {}", status);
                BufferedReader in = new BufferedReader(
                        new InputStreamReader(is));

                StringBuilder response_msg = new StringBuilder();
                String s;
                while((s = in.readLine()) != null) {
                    response_msg.append(s);
                }
                log.info("response: {}", response_msg.toString());

            } catch (IOException e) {
                e.printStackTrace();
            } catch (HttpException e) {
                e.printStackTrace();
            }
        }
        result.setContext(context);
        return result;
    }

    public void removeResource(KiWiResource r) {
        KiWiUriResource subject;
        if(!r.isUriResource()) {
            log.error("The LMF client does not support anonymous resources at the moment. ");
            return;
        } else {
            subject = (KiWiUriResource) r;
        }

        URI uri = configurationService.getResourceConnection(subject);
        HttpClient httpClient = new DefaultHttpClient();
        HttpDelete delete = new HttpDelete(uri);

        /*
         curl -i -X DELETE http://localhost:8080/LMF/resource/1234
         */

        try {

            HttpResponse respone = httpClient.execute(delete);
            int status = respone.getStatusLine().getStatusCode();

            InputStream is = respone.getEntity().getContent();
            log.info("status: {}", status);
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(is));

            StringBuilder response = new StringBuilder();
            String s;
            while((s = in.readLine()) != null) {
                response.append(s);
            }
            log.info("response: {}", response.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } catch (HttpException e) {
            e.printStackTrace();
        }
    }

    public KiWiUriResource getUriResource(String uri_str) {
        KiWiUriResource resource = new KiWiUriResource(uri_str);

        URI uri = configurationService.getResourceConnection(resource);
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet get = new HttpGet(uri);
        get.setHeader("Accept", "text/rdf+n3;rel=meta");

        /*
        curl -X GET  -L -i -H "Accept: application/json; rel=meta" \
             http://localhost:8080/LMF/resource/1234
         */

        try {
            HttpResponse response = httpClient.execute(get);

            int status = response.getStatusLine().getStatusCode();

            InputStream is = response.getEntity().getContent();

            log.info("status: {}", status);
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(is));

            log.info("status: {}", status);

            if(status >= 200 && status < 400) {
                return resource;
            } else {
                return null;
            }
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (HttpException e) {
            e.printStackTrace();
        }

        return null;
    }

    public KiWiAnonResource getAnonResource(String id) {
        KiWiAnonResource resource = new KiWiAnonResource(id);

        URI uri = configurationService.getResourceConnection(resource);
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet get = new HttpGet(uri);
        get.setHeader("Accept", "text/rdf+n3;rel=meta");

        /*
        curl -X GET  -L -i -H "Accept: application/json; rel=meta" \
             http://localhost:8080/LMF/resource/1234
         */
        
        try {
            HttpResponse response = httpClient.execute(get);

            int status = response.getStatusLine().getStatusCode();

            InputStream is = response.getEntity().getContent();

            log.info("status: {}", status);
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(is));

            log.info("status: {}", status);

            if(status >= 200 && status < 400) {
                return resource; 
            } else {
                return null;
            }
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (HttpException e) {
            e.printStackTrace();
        }

        return null;
    }

    public String getProperty(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return null;
            }

            if(property_uri.equals(expected_uri)) {
                if(t.getObject().isLiteral()) {
                    return ((KiWiLiteral)t.getObject()).getContent(); 
                } 
            }
        }
        return null;
    }

    public String getProperty(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return null;
            }

            if(property_uri.equals(expected_uri) && t.getContext().equals(context)) {
                if(t.getObject().isLiteral()) {
                    return ((KiWiLiteral)t.getObject()).getContent();
                }
            }
        }
        return null;
    }

    public String getProperty(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return null;
            }

            if(property_uri.equals(expected_uri)) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    if(literal.getLanguage() != null && literal.getLanguage().equals(loc)) {
                        return literal.getContent();
                    }
                }
            }
        }
        return null;
    }

    public String getProperty(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return null;
            }

            if(property_uri.equals(expected_uri) &&
                    (t.getContext() != null && t.getContext().equals(context))) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    if(literal.getLanguage() != null && literal.getLanguage().equals(loc)) {
                        return literal.getContent();
                    }
                }
            }
        }
        return null;
    }

    public Iterable<String> getProperties(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        Set<String> properties = new HashSet<String>(); 
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return Collections.emptySet();
            }

            if(property_uri.equals(expected_uri)) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    properties.add(literal.getContent());
                }
            }
        }
        return properties;
    }

    public Iterable<String> getProperties(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        Set<String> properties = new HashSet<String>();
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return Collections.emptySet();
            }

            if(property_uri.equals(expected_uri) &&
                    (t.getContext() != null && t.getContext().equals(context))) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    properties.add(literal.getContent());
                }
            }
        }
        return properties;
    }

    public Iterable<String> getProperties(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        Set<String> properties = new HashSet<String>();
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();
            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return Collections.emptySet();
            }

            if(property_uri.equals(expected_uri)) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    if(literal.getLanguage() != null && literal.getLanguage().equals(loc)) {
                        properties.add(literal.getContent());
                    }
                }
            }
        }
        return properties;
    }

    public Iterable<String> getProperties(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        Set<String> properties = new HashSet<String>();
        for(KiWiTriple t : triples) {
            String property_uri = t.getProperty().getUri();

            String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            if(expected_uri == null) {
                log.error("Could not resolve property {}", propLabel);
                return Collections.emptySet();
            }

            if(property_uri.equals(expected_uri) &&
                    (t.getContext() != null && t.getContext().equals(context))) {
                if(t.getObject().isLiteral()) {
                    KiWiLiteral literal = (KiWiLiteral) t.getObject();
                    if(literal.getLanguage() != null && literal.getLanguage().equals(loc)) {
                        properties.add(literal.getContent());
                    }
                }
            }
        }
        return properties;
    }

    public void setProperty(KiWiResource r, String propLabel, String propValue, KiWiUriResource context) throws NamespaceResolvingException {
        removeProperty(r, propLabel, context);
        KiWiLiteral literal = literalService.createLiteral(propValue);
        String prop_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        if(prop_uri == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        KiWiUriResource property_resource = createUriResource(prop_uri);

        if(property_resource == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }

        tripleStore.createTriple(r, property_resource, literal, context);
    }

    public void setProperty(KiWiResource r, String propLabel, String propValue) throws NamespaceResolvingException {
        removeProperty(r, propLabel);
        KiWiLiteral literal = literalService.createLiteral(propValue);
        String prop_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        if(prop_uri == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        KiWiUriResource property_resource = createUriResource(prop_uri);

        if(property_resource == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        tripleStore.createTriple(r, property_resource, literal, null);
    }

    public <T> void setProperty(KiWiResource r, String propLabel, T propValue, Locale loc) throws NamespaceResolvingException {
        removeProperty(r, propLabel, loc);
        KiWiLiteral literal = literalService.createLiteral(propValue);
        String prop_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        if(prop_uri == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        KiWiUriResource property_resource = createUriResource(prop_uri);

        if(property_resource == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        tripleStore.createTriple(r, property_resource, literal, null);
    }

    public <T> void setProperty(KiWiResource r, String propLabel, T propValue, Locale loc, KiWiUriResource context) throws NamespaceResolvingException {
        removeProperty(r, propLabel, loc, context);
        KiWiLiteral literal = literalService.createLiteral(propValue);
        literal.setLanguage(loc);
        String prop_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        if(prop_uri == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        KiWiUriResource property_resource = createUriResource(prop_uri);

        if(property_resource == null) {
            log.error("Could not set property {} for resource {}", propLabel, r.toString());
            return;
        }
        tripleStore.createTriple(r, property_resource, literal, context);
    }

    public boolean removeProperty(KiWiResource r, String propLabel) throws NamespaceResolvingException {
        try {
            boolean found = false;
            Iterable<String> properties = getProperties(r, propLabel);
            String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            KiWiUriResource prop = getUriResource(uri);
            if(prop == null) {
                return true;
            }
            
            for(String p : properties) {
                KiWiLiteral literal = literalService.createLiteral(p);
                tripleStore.removeTriple(r, prop, literal, null);
                found = true;
            }
            return found;
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean removeProperty(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException {
        try {
            boolean found = false;
            Iterable<String> properties = getProperties(r, propLabel, context);
            String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            KiWiUriResource prop = getUriResource(uri);
            if(prop == null) {
                return true;
            }

            for(String p : properties) {
                KiWiLiteral literal = literalService.createLiteral(p);
                tripleStore.removeTriple(r, prop, literal, context);
                found = true;
            }
            return found;
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean removeProperty(KiWiResource r, String propLabel, Locale loc, KiWiUriResource context) throws NamespaceResolvingException {
        try {
            boolean found = false;
            Iterable<String> properties = getProperties(r, propLabel, loc, context);
            String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            KiWiUriResource prop = getUriResource(uri);
            if(prop == null) {
                return true;
            }

            for(String p : properties) {
                KiWiLiteral literal = literalService.createLiteral(p);
                tripleStore.removeTriple(r, prop, literal, context);
                found = true;
            }
            return found;
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean removeProperty(KiWiResource r, String propLabel, Locale loc) throws NamespaceResolvingException {
        try {
            boolean found = false;
            Iterable<String> properties = getProperties(r, propLabel, loc);
            String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

            KiWiUriResource prop = getUriResource(uri);
            if(prop == null) {
                return true;
            }

            for(String p : properties) {
                KiWiLiteral literal = literalService.createLiteral(p);
                tripleStore.removeTriple(r, prop, literal, null);
                found = true;
            }
            return found;
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return false;
    }

    public List<KiWiTriple> listOutgoing(KiWiResource r) {
        Set<KiWiTriple> triples = tripleStore.listTriples(r);
        List<KiWiTriple> ret = new ArrayList<KiWiTriple>(triples);
        return ret;
    }

    public List<KiWiTriple> listOutgoing(KiWiResource r, KiWiUriResource context) {
        Set<KiWiTriple> triples = tripleStore.listTriples(r, context);
        List<KiWiTriple> ret = new ArrayList<KiWiTriple>(triples);
        return ret;
    }

    public List<KiWiTriple> listOutgoing(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiUriResource property = getUriResource(propLabel);
        if(property == null) {
            return Collections.emptyList();
        }
        Set<KiWiTriple> triples = tripleStore.listTriples(r, property);
        List<KiWiTriple> ret = new ArrayList<KiWiTriple>(triples);
        return ret;
    }

    public List<KiWiTriple> listOutgoing(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiTriple> triples = tripleStore.listTriples(r, context);
        List<KiWiTriple> ret = new ArrayList<KiWiTriple>(triples);

        String expected_uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);
        if(expected_uri == null) {
            log.error("Could not resolve property {}", propLabel);
            return Collections.emptyList();
        }
        for(KiWiTriple t : triples) {
            if(expected_uri.equals(t.getProperty().getUri())) {
                ret.add(t);
            }
        }

        return ret;
    }

    public Iterable<KiWiNode> listOutgoingNodes(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiUriResource property = getUriResource(propLabel);
        if(property == null) {
            return Collections.emptyList();
        }
        Set<KiWiTriple> triples = tripleStore.listTriples(r, property);
        Set<KiWiNode> ret = new HashSet<KiWiNode>();

        for(KiWiTriple t : triples) {
            ret.add(t.getObject());
        }

        return ret;
    }

    public Iterable<KiWiNode> listOutgoingNodes(KiWiResource r, String propLabel, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        KiWiUriResource property = getUriResource(propLabel);
        if(property == null) {
            return Collections.emptyList();
        }
        Set<KiWiTriple> triples = tripleStore.listTriples(r, property, context);
        Set<KiWiNode> ret = new HashSet<KiWiNode>();

        for(KiWiTriple t : triples) {
            ret.add(t.getObject());
        }

        return ret;
    }

    public <C extends KiWiNode> void setOutgoingNode(KiWiResource r, String propLabel, C target) throws NamespaceResolvingException, ResourceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        KiWiUriResource prop = createUriResource(uri);

        Set<KiWiTriple> existing_triples = tripleStore.listTriples(r, prop);

        for(KiWiTriple t : existing_triples) {
            tripleStore.removeTriple(t);
        }

        tripleStore.createTriple(r, prop, target, null);
    }

    public <C extends KiWiNode> void setOutgoingNode(KiWiResource r, String propLabel, C target, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        KiWiUriResource prop = createUriResource(uri);

        Set<KiWiTriple> existing_triples = tripleStore.listTriples(r, prop, context);

        for(KiWiTriple t : existing_triples) {
            tripleStore.removeTriple(t);
        }

        tripleStore.createTriple(r, prop, target, context);
    }

    public void addOutgoingNode(KiWiResource r, String propLabel, KiWiNode target) throws NamespaceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        KiWiUriResource prop = createUriResource(uri);

        addOutgoingNode(r, prop, target);
    }

    public void addOutgoingNode(KiWiResource r, KiWiUriResource prop, KiWiNode target) throws NamespaceResolvingException {
        tripleStore.createTriple(r, prop, target, null);
    }

    public void addOutgoingNode(KiWiResource r, String propLabel, KiWiNode target, KiWiUriResource context) throws NamespaceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);

        KiWiUriResource prop = createUriResource(uri);

        addOutgoingNode(r, prop, target);
    }

    public void addOutgoingNode(KiWiResource r, KiWiUriResource prop, KiWiNode target, KiWiUriResource context) throws NamespaceResolvingException {
        tripleStore.createTriple(r, prop, target, context);
    }

    public void removeOutgoingNode(KiWiResource r, String propLabel, KiWiResource target) throws NamespaceResolvingException {
        removeOutgoingNode(r, propLabel, target, null);
    }

    public void removeOutgoingNode(KiWiResource r, String propLabel, KiWiResource target, KiWiUriResource context) throws NamespaceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);
        KiWiUriResource prop = getUriResource(uri);
        if(prop == null) {
            return;
        }
        Set<KiWiTriple> existing_triples = tripleStore.listTriples(r, prop, target, context);

        for(KiWiTriple t : existing_triples) {
            tripleStore.removeTriple(t);
        }
    }

    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r) {
        try {
            return listIncoming(r, null);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }

    @Override
    public List<KiWiTriple> listIncoming(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        HttpURLConnection con = configurationService.getSPARQLEndpointConnection();
        try {

            OutputStream out = con.getOutputStream();

            con.setDoOutput(true);
            con.setRequestMethod("POST");

            OutputStreamWriter outw = new OutputStreamWriter(con.getOutputStream());

            String property = "?p";
            if(propLabel != null) {
                property = "<" + KiWiNamespaceUtils.resolvePropLabel(propLabel) + ">";
            }

            StringBuilder qBuilder = new StringBuilder();
            qBuilder.append("CONSTRUCT { ?s ");
                qBuilder.append(property);
                qBuilder.append(" <");
                if(r.isUriResource()) {
                    qBuilder.append(((KiWiUriResource)r).getUri());
                } else {
                    // TODO
                }
                qBuilder.append("> } ");
            qBuilder.append("WHERE { ?s ");
                qBuilder.append(property);
                qBuilder.append(" <");
            if(r.isUriResource()) {
                qBuilder.append(((KiWiUriResource)r).getUri());
            } else {
                // TODO
            }
            qBuilder.append("> } ");

            outw.append("query=");
            outw.append(qBuilder.toString());
            outw.append("&output=n3");

            out.close();

            int status = con.getResponseCode();

            log.info("status: {}", status);
            BufferedReader in = new BufferedReader(
                    new InputStreamReader(
                    con.getInputStream()));
            if(con.getInputStream().markSupported())
                con.getInputStream().mark(1024);
            else {
                log.error("Marking of BufferedReader not supported. Fix this!");
                return Collections.emptyList();
            }

            StringBuilder response = new StringBuilder();
            String s;
            while((s = in.readLine()) != null) {
                response.append(s);
            }
            log.info("response: {}", response.toString());

            con.getInputStream().reset();

            Repository myRepository = new SailRepository(new MemoryStore());
            try {
                myRepository.initialize();
                RepositoryConnection repCon = myRepository.getConnection();
                repCon.add(con.getInputStream(), null, RDFFormat.N3);
                repCon.commit();

                RepositoryResult<Statement> statements = repCon.getStatements(null, null, null, true);
                List<KiWiTriple> triples = new ArrayList<KiWiTriple>();
                while(statements.hasNext()) {
                    Statement statement = statements.next();
                    String sub_str = statement.getSubject().stringValue();
                    log.debug("sub str: {}", sub_str);
                    KiWiResource subject_resource = new KiWiUriResource(sub_str);

                    String prop_str = statement.getPredicate().stringValue();
                    log.debug("prop str: {}", sub_str);
                    KiWiUriResource property_resource = new KiWiUriResource(prop_str);

                    String obj_str = statement.getObject().stringValue();
                    log.debug("obj str: {}", obj_str);
                    // TODO: if obj str == "..." || obj str == uri::...
                    //KiWiUriResource object_property = new KiWiUriResource(obj_str);
                    // TODO: if obj_str typeOf String/int/Date/etc.
                    KiWiLiteral object_literal = new KiWiStringLiteral(obj_str);

                    String context_str = statement.getContext().stringValue();
                    log.debug("context str: {}", context_str);
                    KiWiUriResource context_resource = new KiWiUriResource(context_str);

                    KiWiTriple t = new KiWiTriple(subject_resource, property_resource, object_literal, context_resource);
                    triples.add(t);
                }

                return triples;
            } catch (RepositoryException e) {
                e.printStackTrace();
            } catch (RDFParseException e) {
                e.printStackTrace();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return Collections.emptyList();
    }

    @Override
    public Iterable<KiWiResource> listIncomingNodes(KiWiResource r, String propLabel) throws NamespaceResolvingException, ResourceResolvingException {
        Set<KiWiResource> resources = new HashSet<KiWiResource>();
        List<KiWiTriple> incomingTriples = listIncoming(r, propLabel);
        for(KiWiTriple t : incomingTriples) {
            resources.add(t.getSubject());
        }
        return resources;
    }

    @Override
    public void addIncomingNode(KiWiResource r, String propLabel, KiWiResource source) throws NamespaceResolvingException, ResourceResolvingException {
        addIncomingNode(r, propLabel, source, null);
    }

    @Override
    public void addIncomingNode(KiWiResource r, String propLabel, KiWiResource source, KiWiUriResource context) throws NamespaceResolvingException, ResourceResolvingException {
        String uri = KiWiNamespaceUtils.resolvePropLabel(propLabel);
        KiWiUriResource prop = createUriResource(uri);
        tripleStore.createTriple(source, prop, r, context);
    }

    public String getLabel(KiWiResource r) {
        try {
            String label = getProperty(r, "rdfs:label");
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getLabel(KiWiResource r, KiWiUriResource context) {
        try {
            String label = getProperty(r, "rdfs:label", context);
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getLabel(KiWiResource r, Locale loc) {
        try {
            String label = getProperty(r, "rdfs:label", loc);
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getLabel(KiWiResource r, Locale loc, KiWiUriResource context) {
        try {
            String label = getProperty(r, "rdfs:label", loc, context);
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setLabel(KiWiResource r, Locale loc, String label) {
        try {
            setProperty(r, "rdfs:label", label, loc);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public void setLabel(KiWiResource r, Locale loc, String label, KiWiUriResource context) {
        try {
            setProperty(r, "rdfs:label", label, loc, context);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public String getComment(KiWiResource r, Locale loc) {
        try {
            String label = getProperty(r, "rdfs:comment", loc);
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public String getComment(KiWiResource r, Locale loc, KiWiUriResource context) {
        try {
            String label = getProperty(r, "rdfs:comment", loc, context);
            return label;
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void setComment(KiWiResource r, Locale loc, String comment) {
        try {
            setProperty(r, "rdfs:comment", comment, loc);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public void setComment(KiWiResource r, Locale loc, String comment, KiWiUriResource context) {
        try {
            setProperty(r, "rdfs:comment", comment, loc, context);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public Collection<KiWiResource> getTypes(KiWiResource r) {
        Set<KiWiResource> types = new HashSet<KiWiResource>();
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type");
            for(KiWiNode n : type_nodes) {
                if(n.isAnonymousResource() || n.isUriResource()) {
                    types.add((KiWiResource) n);
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return types;
    }

    public Collection<KiWiResource> getTypes(KiWiResource r, KiWiUriResource context) {
        Set<KiWiResource> types = new HashSet<KiWiResource>();
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type", context);
            for(KiWiNode n : type_nodes) {
                if(n.isAnonymousResource() || n.isUriResource()) {
                    types.add((KiWiResource) n);
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return types;
    }

    public void addType(KiWiResource r, KiWiResource type, KiWiUriResource context) {
        try {
            addOutgoingNode(r, "rdf:type", type, context);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public void addType(KiWiResource r, KiWiResource type) {
        try {
            addOutgoingNode(r, "rdf:type", type);
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        }
    }

    public void removeType(KiWiResource r, KiWiResource type) {
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type");
            for(KiWiNode n : type_nodes) {
                if(n.isAnonymousResource() || n.isUriResource()) {
                    removeOutgoingNode(r, "rdf:type", (KiWiResource) n);
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
    }

    public void removeType(KiWiResource r, KiWiResource type, KiWiUriResource context) {
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type", context);
            for(KiWiNode n : type_nodes) {
                if(n.isAnonymousResource() || n.isUriResource()) {
                    removeOutgoingNode(r, "rdf:type", (KiWiResource) n, context);
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
    }

    public boolean hasType(KiWiResource r, String typeUri) {
        boolean found = false;
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type");
            for(KiWiNode n : type_nodes) {
                if(n.isUriResource() && ((KiWiUriResource) n).getUri().equals(typeUri)) {
                    found = true;
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return found;
    }

    public boolean hasType(KiWiResource r, String typeUri, KiWiUriResource context) {
        boolean found = false;
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type", context);
            for(KiWiNode n : type_nodes) {
                if(n.isUriResource() && ((KiWiUriResource) n).getUri().equals(typeUri)) {
                    found = true;
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return found;
    }

    public boolean hasType(KiWiResource r, KiWiUriResource type) {
        if(type == null) {
            log.error("Type 'null' is invalid");
            return false;
        }

        boolean found = false;
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type");
            for(KiWiNode n : type_nodes) {
                if(n.isUriResource() && ((KiWiUriResource) n).getUri().equals(type.getUri())) {
                    found = true;
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return found;
    }

    public boolean hasType(KiWiResource r, KiWiUriResource type, KiWiUriResource context) {
        if(type == null) {
            log.error("Type 'null' is invalid");
            return false;
        }

        boolean found = false;
        try {

            Iterable<KiWiNode> type_nodes = listOutgoingNodes(r, "rdf:type", context);
            for(KiWiNode n : type_nodes) {
                if(n.isUriResource() && ((KiWiUriResource) n).getUri().equals(type.getUri())) {
                    found = true;
                }
            }
        } catch (NamespaceResolvingException e) {
            e.printStackTrace();
        } catch (ResourceResolvingException e) {
            e.printStackTrace();
        }
        return found;
    }

    // TODO: implement caches
    public void clearCaches() {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}
